package com.radar.io.cinrad;

import cn.hutool.core.date.DateUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.radar.algorithm.MathCommon;
import com.radar.common.CinradType;
import com.radar.common.DateConstants;
import com.radar.common.GisConstants;
import com.radar.common.ProductType;
import com.radar.exception.RadarDecodeError;
import com.radar.utils.BitConverter;
import ucar.ma2.ArrayByte;
import ucar.ma2.ArrayDouble;
import ucar.ma2.DataType;
import ucar.nc2.Attribute;
import ucar.nc2.Dimension;
import ucar.nc2.Variable;

import java.io.IOException;
import java.util.*;
import java.util.stream.IntStream;

/**
 * @author: fy-wbj
 * @description: CC 波段 雷达
 * @date: 2020/10/10
 */
public class RadarCC extends BaseCinrad {

    private double[] ele;

    private double[][] azi;

    private double reso = 300;

    private Date scanEndTime;

    double maxL;

    static Log log = LogFactory.get();

    public RadarCC(String file, CinradType type, String code) {
        super(file, type, code);
    }

    public void parse(byte[] bytes) throws RadarDecodeError {
        RadarHeader header = new RadarHeader();
        int hLen = header.size;
        try {
            long nSize = bytes.length;
            RadarData data = new RadarData();
            if ((nSize - hLen) % data.size != 0) {
                log.error("cc 数据异常");
                return;
            }
            int index = 0;
            byte[] bytdata = new byte[hLen];
            System.arraycopy(bytes, index, bytdata, 0, bytdata.length);
            index += bytdata.length;
            header.parse(bytdata);
            vcp = header.header1.ucScanMode;
            int layers = 0;
            if (vcp == 1) {
                layers = 1;
            }
            if (vcp == 10) {
                layers = 1;
            }
            if (vcp > 100) {
                layers = vcp - 100;
            }
            GregorianCalendar gc = new GregorianCalendar();
//            gc.set(header.header1.ucSYear1 * 100 + header.header1.ucSYear2, header.header1.ucSMonth, header.header1.ucSDay, header.header1.ucSHour, header.header1.ucSMinute, header.header1.ucSSecond);
//            this.dateTime = gc.getTime();
            String format = header.header1.ucEYear1 * 100 + header.header1.ucEYear2 + "-" +
                    (header.header1.ucEMonth < 10 ? "0" + header.header1.ucEMonth : header.header1.ucEMonth) + "-" +
                    (header.header1.ucEDay < 10 ? "0" + header.header1.ucEDay : header.header1.ucEDay);
            Date date = DateUtil.parse(format, DateConstants.DATE_FORMAT_YY_MM_DD);
            gc.setTime(date);
            gc.set(GregorianCalendar.HOUR_OF_DAY, header.header1.ucEHour);
            gc.set(GregorianCalendar.MINUTE, header.header1.ucEMinute);
            gc.set(GregorianCalendar.SECOND, header.header1.ucESecond);
            scanEndTime = gc.getTime();
            this.dateTime = scanEndTime;

            this.maxL = header.LayerInfo[0].usMaxL * 10;
//            reso = header.LayerInfo[0].usBindWidth;
            int[] records = new int[layers];
            double[] nyquist_v = new double[layers];
            this.ele = new double[layers];
            for (int lev = 0; lev < layers; lev++) {
                records[lev] = header.LayerInfo[lev].usRecordNumber;
                nyquist_v[lev] = header.LayerInfo[lev].usMaxV * 0.01;
                this.ele[lev] = header.LayerInfo[lev].usAngle * 0.01;
            }
            int MaxRecord = Arrays.stream(records).max().getAsInt();
            int bins = 500;
            azi = new double[layers][MaxRecord];
            byte[][][] z = new byte[layers][MaxRecord][bins];
            byte[][][] v = new byte[layers][MaxRecord][bins];
            byte[][][] w = new byte[layers][MaxRecord][bins];
            for (int i = 0; i < layers; i++) {
                for (int j = 0; j < records[i]; j++) {
                    MathCommon.fill(z[i][j]);
                    MathCommon.fill(v[i][j]);
                    MathCommon.fill(w[i][j]);
                    bytdata = new byte[data.size];
                    System.arraycopy(bytes, index, bytdata, 0, bytdata.length);
                    index += bytdata.length;
                    data.parse(bytdata);
                    azi[i][j] = j * 360.0 / 512.0;
                    for (int m = 0; m < bins; m++) {
                        if (data.Z[m] != -32768) {
                            z[i][j][m] = MathCommon.CodeRef(data.Z[m] / 10.0);
                            v[i][j][m] = MathCommon.CodeV(data.V[m] / 10.0);
                            w[i][j][m] = MathCommon.CodeV(data.W[m] / 10.0);
                        }
                    }
                }
            }
            this.initNetcdfTemp();
            this.writeBasicData(z, v, w, nyquist_v);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("cc handle error");
        }
    }

    @Override
    public double[] getElevation() {
        return ele;
    }

    @Override
    public int[] getElevationIndex(ProductType type) {
        return IntStream.range(0, ele.length).toArray();
    }

    @Override
    public double[][] getAzimuth() {
        return azi;
    }

    @Override
    public double getResolution(ProductType type) {
        return reso;
    }

    public void writeBasicData(byte[][][] z, byte[][][] v, byte[][][] w, double[] nyquist_v) throws IOException {
        int dlength = z[0][0].length;
        write.setRedefineMode(true);
        Variable v1;
        if ((v1 = this.netcdf.findVariable("distince")) == null) {
            write.addDimension(null, "distince", dlength);
            v1 = write.addVariable(null, "distince", DataType.DOUBLE, "distince");
            v1.addAttribute(new Attribute("units", "m"));
            v1.addAttribute(new Attribute("resolution", reso));
        }

        List<Dimension> dimensions = new ArrayList<>();
        dimensions.add(netcdf.findDimension("elevation"));
        dimensions.add(netcdf.findDimension("azimuth"));
        dimensions.add(netcdf.findDimension("distince"));

        Variable rVariable = ProductType.REF.writeVariable(write, netcdf, dimensions);
        Variable vVariable = ProductType.VEL.writeVariable(write, netcdf, dimensions);
        Variable wVariable = ProductType.SW.writeVariable(write, netcdf, dimensions);

        // 不模糊速度
        Variable v3;
        if ((v3 = this.netcdf.findVariable("NYQUIST_V")) == null) {
            v3 = write.addVariable(null, "NYQUIST_V", DataType.DOUBLE, "elevation");
            v3.addAttribute(new Attribute("missing_value", GisConstants.UNDEF));
        }
        ArrayDouble rDistances = new ArrayDouble.D1(dlength);

        IntStream.range(0, dlength).forEach(i -> rDistances.setDouble(i, (i + 1) * reso));

        ArrayByte rArray = MathCommon.toByteArray(z);
        ArrayByte wArray = MathCommon.toByteArray(w);
        ArrayByte vArray = MathCommon.toByteArray(v);
        rVariable.setCachedData(rArray);
        vVariable.setCachedData(vArray);
        wVariable.setCachedData(wArray);

        v1.setCachedData(rDistances);
        ArrayDouble a3 = new ArrayDouble.D1(ele.length);
        IntStream.range(0, ele.length).forEach(i -> a3.setDouble(i, nyquist_v[i]));

        v3.setCachedData(a3);
    }

    //region 原始数据文件

    public static class TagWeatherRadar1 {
        int size = 218;
        String cFileType;                        //3830数据标识(CINRADC)
        String cCountry;                        //国家名
        String cProvince;                        //省名
        String cStation;                        //站名
        String cStationNumber;                //区站号
        String cRadarType;                    //雷达型号
        String cLongitude;                    //天线所在经度
        String cLatitude;                        //天线所在纬度
        int lLongitudeValue;                    //具体经度
        int lLatitudeValue;                    //具体纬度
        int lHeight;                            //天线海拔高度
        short sMaxAngle;                        //地物阻挡最大仰角
        short sOptAngle;                        //最佳观测仰角
        byte ucSYear1;                    //观测开始时间的年千百位(19-20)
        byte ucSYear2;                    //观测开始时间的年十个位(00-99)
        byte ucSMonth;                //观测开始时间的月(1-12)
        byte ucSDay;                    //观测开始时间的日(1-31)
        byte ucSHour;                    //观测开始时间的时(0-23)
        byte ucSMinute;                //观测开始时间的分(0-59)
        byte ucSSecond;                //观测开始时间的秒(0-59)
        byte ucTimeFrom;                //时间来源 0-计算机时钟(1天内未对时) 1-计算机时钟(1天内已对时) 2-GPS  3-其它
        byte ucEYear1;                    //观测结束时间的年千百位(19-20)
        byte ucEYear2;                    //观测结束时间的年十个位(00-99)
        byte ucEMonth;                //观测结束时间的月(1-12)
        byte ucEDay;                    //观测结束时间的日(1-31)
        byte ucEHour;                    //观测结束时间的时(0-23)
        byte ucEMinute;                //观测结束时间的分(0-59)
        byte ucESecond;                //观测结束时间的秒(0-59)
        byte ucScanMode;                //扫描方式  1-RHI 10-PPI和ZPPI 1XX=VPPI(XX为扫描圈数)
        int ulSmilliSecond;            //以微秒为单位表示的秒的小数位
        int usRHIA;                    //RHI所在的方位角(0.01度为单位) PPI和VPPI时为FFFF
        short sRHIL;                            //RHI所在的最低仰角(0.01度为单位) PPI和VPPI时为FFFF
        short sRHIH;                            //RHI所在的最高仰角(0.01度为单位) PPI和VPPI时为FFFF
        int usEchoType;        //回波类型  0x405a-Z  0x406a-V  0x407a-W 0x408a-ZVW三要素
        int usProdCode;        //数据类型  0x8001-PPI数据  0x8002-RHI数据 x8003-VPPI数据  0x8004-单强度PPI数据 0x8005-CAPPI数据
        byte ucCalibration;                //标校状态  0-无  1-自动  2-1星期内人工 3-1月内人工
        String remain1;                //保留字

        public void parse(byte[] b) throws Exception {
            int index = 0;
            cFileType = new String(Arrays.copyOfRange(b, index, 16 + index), "GBK");
            index += 16;
            cCountry = new String(Arrays.copyOfRange(b, index, 30 + index), "GBK");
            index += 30;
            cProvince = new String(Arrays.copyOfRange(b, index, 20 + index), "GBK");
            index += 20;
            cStation = new String(Arrays.copyOfRange(b, index, 40 + index), "GBK");
            index += 40;
            cStationNumber = new String(Arrays.copyOfRange(b, index, 10 + index), "GBK");
            index += 10;
            cRadarType = new String(Arrays.copyOfRange(b, index, 20 + index), "GBK");
            index += 20;
            cLongitude = new String(Arrays.copyOfRange(b, index, 16 + index), "GBK");
            index += 16;
            cLatitude = new String(Arrays.copyOfRange(b, index, 16 + index), "GBK");
            index += 16;
            lLongitudeValue = BitConverter.toInt32(b, index);
            index += 4;
            lLatitudeValue = BitConverter.toInt32(b, index);
            index += 4;
            lHeight = BitConverter.toInt32(b, index);
            index += 4;
            sMaxAngle = BitConverter.toInt16(b, index);
            index += 2;
            sOptAngle = BitConverter.toInt16(b, index);
            index += 2;
            ucSYear1 = b[index];
            index++;
            ucSYear2 = b[index];
            index++;
            ucSMonth = b[index];
            index++;
            ucSDay = b[index];
            index++;
            ucSHour = b[index];
            index++;
            ucSMinute = b[index];
            index++;
            ucSSecond = b[index];
            index++;
            ucTimeFrom = b[index];
            index++;
            ucEYear1 = b[index];
            index++;
            ucEYear2 = b[index];
            index++;
            ucEMonth = b[index];
            index++;
            ucEDay = b[index];
            index++;
            ucEHour = b[index];
            index++;
            ucEMinute = b[index];
            index++;
            ucESecond = b[index];
            index++;
            ucScanMode = b[index];
            index++;

        }
    }

    public class TagWeatherRadar2 {
        int size = 144;
        int lAntennaG;                        //天线增益(0.001dB)
        int lPower;                            //峰值功率(瓦)
        int lWavelength;                        //波长(微米)
        int usBeamH;                //垂直波束宽度(秒)
        int usBeamL;                //水平波束宽度(秒)
        int usPolarization;                //极化状态 0-水平 1-垂直 2-双偏振 3-圆偏振 4-其它
        int usLogA;                    //对数动态范围(0.01dB)
        int usLineA;                    //线性动态范围(0.01dB)
        int usAGCP;                    //AGC延迟量(微秒)
        int usFreqMode;                //频率方式	1-单重复频率  2-双重复频率3:2 3-双重复频率4:3
        int usFreqRepeat;                //重复频率
        int usPPPPulse;                //PPP脉冲数
        int usFFTPoint;                //FFT间隔点数
        int usProcessType;            //信号处理方式	1-PPP	2-全程FFT 3-单库FFT
        byte ucClutterT;                //杂波消除阀值(即STC)
        byte cSidelobe;                        //第一旁瓣(dB)
        byte ucVelocityT;                //速度门限
        byte ucFilderP;                    //地物消除方式	0-无		1-IIR滤波器1 2-IIR滤波器2	3-IIR滤波器3 4-IIR滤波器4
        byte ucNoiseT;                    //噪声消除阀值(即强度门限)
        byte ucSQIT;                    //SQI门限
        byte ucIntensityC;                //DVIP强度值估算采用的通道 1-对数通道 2-线性通道
        byte ucIntensityR;                //强度值估算是否距离订正 0-无(dB) 1-已订正(dBZ)
        byte ucCalNoise;                //噪声系数标定值
        byte ucCalPower;                //发射功率标定值
        byte ucCalPulseWidth;            //脉冲宽度标定值
        byte ucCalWorkFreq;            //工作频率标定值
        byte ucCalLog;                    //对数斜率标定值
        String remain3;                        //保留字
        int liDataOffset;            //数据偏移地址
    }

    public class TagVppiScanParameter {
        int size = 22;
        int usMaxV;            //最大可测速度(厘米/秒)
        int usMaxL;            //最大可测距离(10米)
        int usBindWidth;        //库长(米)
        int usBinNumber;        //每径向库数
        int usRecordNumber;    //本圈径向数
        int usArotate;        //本圈转速(0.01度/秒)
        int usPrf1;            //本圈第一次重复频率(0.1Hz)对应单重频或双重频的高者
        int usPrf2;            //本圈第二次重复频率(0.1Hz)对应双重频的低者
        int usSpulseW;        //本圈脉宽(微秒)
        short usAngle;            //仰角(0.01度)
        byte cSweepStatus;        //1=单要素	2=三要素(单重频)	3=三要素(双重频)
        byte cAmbiguousp;        //0=无软件退模糊	1=软件退模糊

        public void parse(byte[] b) throws Exception {
            int index = 0;
            usMaxV = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            usMaxL = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            usBindWidth = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            usBinNumber = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            usRecordNumber = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            usArotate = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            usPrf1 = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            usPrf2 = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            usSpulseW = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            usAngle = BitConverter.toInt16(b, index);
            index += 2;
            cSweepStatus = b[index];
            index++;
            cAmbiguousp = b[index];
        }
    }

    public class RadarHeader {
        int size = 1024;
        TagWeatherRadar1 header1;
        TagVppiScanParameter[] LayerInfo;
        short remain2;
        TagWeatherRadar2 header2;

        public void parse(byte[] b) throws Exception {
            int index = 0;
            header1 = new TagWeatherRadar1();
            header1.parse(Arrays.copyOfRange(b, index, header1.size + index));
            index += header1.size;
            LayerInfo = new TagVppiScanParameter[30];
            for (int i = 0; i < 30; i++) {
                LayerInfo[i] = new TagVppiScanParameter();
                LayerInfo[i].parse(Arrays.copyOfRange(b, index, LayerInfo[i].size + index));
                index += LayerInfo[i].size;
            }
        }
    }

    public class RadarData {
        int size = 3000;
        short[] Z;
        short[] V;
        short[] W;

        public void parse(byte[] b) throws Exception {
            int index = 0;
            Z = new short[500];
            V = new short[500];
            W = new short[500];
            for (int i = 0; i < 500; i++) {
                Z[i] = BitConverter.toInt16(b, index);
                V[i] = BitConverter.toInt16(b, index + 1000);
                W[i] = BitConverter.toInt16(b, index + 1000);
                index += 2;
            }
        }
    }

    //endregion

}
